package com.haozi.security.vertify;

import com.haozi.common.Constants;
import com.haozi.security.annon.Sign;
import com.haozi.security.config.SignConfig;
import com.haozi.security.exception.SignParamException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.TreeMap;

/**
 * @author zyh
 * @version 1.0
 * @date 2022/11/5 4:07 下午
 */
@ControllerAdvice
public class SignAdvice implements RequestBodyAdvice {

    @Autowired
    private SignConfig signConfig;
    @Autowired
    SignVertify signVertify;
    private static final Logger log = LoggerFactory.getLogger(SignAdvice.class);
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

        Sign methodAnnotation = methodParameter.getMethodAnnotation(Sign.class);

        return methodAnnotation != null;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        HttpHeaders headers = inputMessage.getHeaders();
        if(CollectionUtils.isEmpty(headers.get(Constants.Key.TIMESTAMP_KEY))){
            log.warn("验签参数错误,{}", headers);
            throw new SignParamException(Constants.Key.TIMESTAMP_KEY);
        }
        if (CollectionUtils.isEmpty(headers.get(Constants.Key.RAND_KEY))) {
            log.warn("验签参数错误,{}", headers);
            throw new SignParamException(Constants.Key.RAND_KEY);
        }
        if (CollectionUtils.isEmpty(headers.get(Constants.Key.SIGN_KEY))) {
            log.warn("验签参数错误,{}", headers);
            throw new SignParamException(Constants.Key.SIGN_KEY);
        }

        InputStream body = inputMessage.getBody();
        String charsetStr = signConfig.getCharset();
        Charset charset = StandardCharsets.UTF_8;
        if (StringUtils.hasLength(charsetStr)) {
            charset = Charset.forName(charsetStr);
        }
        String currentTime = headers.get(Constants.Key.TIMESTAMP_KEY).get(0);

        String bodyParam = StreamUtils.copyToString(body, charset);

        signVertify.verify(bodyParam,signConfig.getSecret(),
                Long.parseLong(currentTime),
                headers.get(Constants.Key.RAND_KEY).get(0),
                headers.get(Constants.Key.SIGN_KEY).get(0));

        return new SignHttpInputMessage(bodyParam.getBytes(charset),inputMessage.getHeaders());
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    class SignHttpInputMessage implements HttpInputMessage {

        public SignHttpInputMessage(byte[] body, HttpHeaders httpHeaders) {
            this.body = body;
            this.httpHeaders = httpHeaders;
        }

        private byte[] body;
        private HttpHeaders httpHeaders;


        @Override
        public InputStream getBody() throws IOException {
            return new ByteArrayInputStream(body);
        }

        @Override
        public HttpHeaders getHeaders() {
            return httpHeaders;
        }
    }
}
